package gogtp

import (
	"errors"
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"sync"
)

/**
 *  GTPCommand
 */
type GTPCommand struct {
	conn    *Connection
	AIName  string
	outSB   strings.Builder
	errSB   strings.Builder
	wait    chan bool
	debug   bool
	locker  sync.Mutex
	version float64
	logHook LogHook
}
type LogHook func(event, data string)

func (c *GTPCommand) OutputF(line string) {
	c.log(RECEIVE, line)
	c.outSB.WriteString(line)
	if strings.Contains(line, "=") || strings.Contains(line, "?") {
		close(c.wait)
	}
}

func (c *GTPCommand) ErrorF(line string) {
	c.errSB.WriteString(line)
}

// 创建命令行
func NewGTPCommand(conn *Connection) (*GTPCommand, error) {
	command := &GTPCommand{
		conn:  conn,
		outSB: strings.Builder{},
		errSB: strings.Builder{},
	}
	r, err := command.Name()
	if err != nil {
		return nil, err
	}
	res, err := command.SendCMD("version")
	if err != nil {
		return nil, err
	}
	v, _ := strconv.ParseFloat(res.Result, 64)
	command.version = v
	command.AIName = r.Result
	return command, nil
}
func (c *GTPCommand) ShowLog(b bool) {
	c.debug = b
}

// 自定义发送
func (c *GTPCommand) SendCMD(cmd string) (Result, error) {
	c.locker.Lock()
	defer func() {
		c.locker.Unlock()
		c.conn.RemoveParse()
	}()
	c.log(SEND, cmd)
	c.outSB.Reset()
	c.errSB.Reset()
	c.wait = make(chan bool)
	if err := c.conn.PostCommand(cmd, c); err != nil {
		return Result{}, err
	}
	<-c.wait

	result, err := c.format()
	if err != nil {
		c.log(ERROR, err.Error())
	}
	return Result{
		Command: cmd,
		Result:  result,
		ErrOut:  c.errSB.String(),
	}, err
}

/**
快捷命令
*/

func (c *GTPCommand) Version() float64 {
	return c.version
}

// KnowCommand 判断命令是否支持
func (c *GTPCommand) KnowCommand(cmd string) bool {
	value, err := c.SendCMD("known_command " + cmd)
	if err != nil {
		return false
	}
	if strings.ToLower(strings.TrimSpace(value.Result)) != "true" {
		return false
	}
	return true
}

// 设置贴目
func (c *GTPCommand) Komi(komi float64) (Result, error) {
	return c.SendCMD(fmt.Sprintf("komi %v", komi))
}

// 设置棋盘大小
func (c *GTPCommand) BoardSize(size int) (Result, error) {
	return c.SendCMD(fmt.Sprintf("boardsize %v", size))
}

// 获取AI落子
func (c *GTPCommand) GenMove(color string) (Result, error) {
	color = strings.ToUpper(color)
	command := "genmove " + color
	return c.SendCMD(command)
}

// 人落子
func (c *GTPCommand) Move(color, coor string) (Result, error) {
	color = strings.ToUpper(color)
	return c.SendCMD(fmt.Sprintf("play %s %s", color, coor))
}

// 加载SGF文件
func (c *GTPCommand) LoadSgf(file string) (Result, error) {
	command := fmt.Sprintf("loadsgf %s", file)
	return c.SendCMD(command)
}

// 获取当前盘面形势判断
func (c *GTPCommand) FinalStatusList(cmd string) (Result, error) {
	command := fmt.Sprintf("final_status_list %s", cmd)
	return c.SendCMD(command)
}

// 设置AI级别
func (c *GTPCommand) SetLevel(seed int) (Result, error) {
	command := fmt.Sprintf("level %d", seed)
	return c.SendCMD(command)
}

// 设置AI随机数
func (c *GTPCommand) SetRandomSeed(seed int) (Result, error) {
	command := fmt.Sprintf("set_random_seed %d", seed)
	return c.SendCMD(command)
}

// 显示棋盘
func (c *GTPCommand) ShowBoard() (Result, error) {
	return c.SendCMD("showboard")
}

// 清空棋盘
func (c *GTPCommand) ClearBoard() (Result, error) {
	return c.SendCMD("clear_board")
}

//打印SGF
func (c *GTPCommand) PrintSgf() (Result, error) {
	return c.SendCMD("printsgf")
}

// 设置时间规则
func (c *GTPCommand) TimeSetting(baseTime, byoTime, byoStones int) (Result, error) {
	return c.SendCMD(fmt.Sprintf("time_settings %d %d %d", baseTime, byoTime, byoStones))
}

// 设置KGS time
func (c *GTPCommand) KGSTimeSetting(mainTime, readTime, readLimit int) (Result, error) {
	return c.SendCMD(fmt.Sprintf("kgs-time_settings byoyomi %d %d %d", mainTime, readTime, readLimit))
}

//获取结果
func (c *GTPCommand) FinalScore() (Result, error) {
	return c.SendCMD("final_score")
}

//悔棋
func (c *GTPCommand) Undo() (Result, error) {
	return c.SendCMD("undo")
}

//设置时间
func (c *GTPCommand) TimeLeft(color string, mainTime, stones int) (Result, error) {
	return c.SendCMD(fmt.Sprintf("time_left %s %d %d", color, mainTime, stones))
}

func (c *GTPCommand) Name() (Result, error) {
	return c.SendCMD("name")
}

//退出
func (c *GTPCommand) Quit() (Result, error) {
	r, err := c.SendCMD("Quit")
	c.conn.Close()
	return r, err
}

func (c *GTPCommand) format() (string, error) {
	result := c.outSB.String()
	regParse := regexp.MustCompile(`([=?]).*\n*`)
	result = regParse.FindString(result)
	res := strings.Split(result, "")
	l := len(res)
	if l > 0 {
		if res[l-1] == "\n" {
			result = strings.Join(res[:l-1], "")
		}
		if len(result) == 0 {
			return "", errors.New("ERROR length=0")
		}
		if res[0] == "=" {
			return strings.TrimSpace(strings.Join(res[1:], "")), nil
		}
		if res[0] == "?" {
			return "", errors.New(strings.Join(res[1:], ""))
		}
	}
	return "", errors.New("ERROR: Unrecognized answer: " + result)
}
func (c *GTPCommand) LogHook(hook LogHook) {
	c.logHook = hook
}

func (c *GTPCommand) log(event int, data string) {
	if c.logHook != nil {
		evStr := ""
		switch event {
		case RECEIVE:
			evStr = "Receive"
		case SEND:
			evStr = "Send"
		case ERROR:
			evStr = "Error"
		default:
			evStr = ""
		}
		c.logHook(evStr, data)
	}
}
